
/****************************************************************************/

int kernelMultitaskerKillProcess(int processId, int force)
{
	// This function should be used to properly kill a process.  This
	// will deallocate all of the internal resources used by the 
	// multitasker in maintaining the process and all of its processes

	// This function should be used to properly kill a process.  This
	// will deallocate all of the internal resources used by the 
	// multitasker in maintaining the process and all of its children.
	// This function will commonly employ a recursive tactic for
	// killing processes with spawned children.  Returns 0 on success,
	// negative on error.

	int status = 0;
	kernelProcess *killProcess = NULL;
	int count;

	// Make sure multitasking has been enabled
	if (!multitaskingEnabled)
		return (status = ERR_NOTINITIALIZED);

	ACQUIRE_LOCK_PROCESS_QUEUE();

	// OK, we need to find the process structure based on the Id we were passed
	killProcess = getProcessById(processId);
	if (killProcess == NULL)
	{
		// There's no such process
		RELEASE_LOCK_PROCESS_QUEUE();
		return (status = ERR_NOSUCHPROCESS);
	}

	// Processes are not allowed to actually kill themselves.  They must use
	// the terminate function to do it normally.
	if (killProcess == kernelCurrentProcess)
	{
		RELEASE_LOCK_PROCESS_QUEUE();
		kernelMultitaskerTerminate(0);
	}

	// Permission check.  A privileged process can kill any other process,
	// but a non-privileged process can only kill processes owned by the
	// same user
	if (kernelCurrentProcess->privilege != PRIVILEGE_SUPERVISOR)
	{
		if (kernelCurrentProcess->userId != killProcess->userId)
		{
			RELEASE_LOCK_PROCESS_QUEUE();
			return (status = ERR_PERMISSION);
		}
	}

	// You can't kill the kernel on purpose
	if (killProcess == kernelProc)
	{
		RELEASE_LOCK_PROCESS_QUEUE();
		kernelError(kernel_error, "It's not possible to kill the kernel process");
		return (status = ERR_INVALID);
	}

	// You can't kill the exception handler thread on purpose
	if (killProcess == exceptionProc)
	{
		RELEASE_LOCK_PROCESS_QUEUE();
		kernelError(kernel_error, "It's not possible to kill the exception thread");
		return (status = ERR_INVALID);
	}

	// If a thread is trying to kill its parent, we won't do that here.
	// Instead we will mark it as 'finished' and let the kernel clean us
	// all up later
	if ((kernelCurrentProcess->type == proc_thread) &&
	    (processId == kernelCurrentProcess->parentProcessId))
	{
		killProcess->state = proc_finished;
		RELEASE_LOCK_PROCESS_QUEUE();
		while (1)
		{
			kernelMultitaskerYield();
		}
	}

	// The request is legitimate.

	// Mark the process as stopped in the process queue, so that the
	// scheduler will not inadvertently select it to run while we're
	// destroying it.
	killProcess->state = proc_stopped;

	// We must loop through the list of existing processes, looking for any 
	// other processes whose states depend on this one (such as child threads
	// who don't have a page directory).  If we remove a process, we need to
	// call this function recursively to kill it (and any of its own dependant
	// children) and then reset the counter, stepping through the list again
	// (since the list will be different after the recursion)

	for (count = 0; count < numQueued; count ++)
	{
		// Is this process blocking on the process we're killing?
		if (processQueue[count]->waitForProcess == processId)
		{
			// This process is blocking on the process we're killing.  If the
			// process being killed was not, in turn, blocking on another
			// process, the blocked process will be made runnable.  Otherwise,
			// the blocked process will be forced to block on the same
			// process as the one being killed.
			if (killProcess->waitForProcess != 0)
			{
				processQueue[count]->waitForProcess = killProcess->waitForProcess;
			}
			else
			{
				processQueue[count]->blockingExitCode = ERR_KILLED;
				processQueue[count]->waitForProcess = 0;
				processQueue[count]->state = proc_ready;
			}
			continue;
		}

		// If this process is a child thread of the process we're killing,
		// or if the process we're killing was blocking on this process,
		// kill it first.
		if ((processQueue[count]->state != proc_finished) &&
		    (processQueue[count]->parentProcessId == killProcess->processId) &&
		    ((processQueue[count]->type == proc_thread) ||
		     (killProcess->waitForProcess == processQueue[count]->processId)))
		{
			status = kernelMultitaskerKillProcess(processQueue[count]->processId, force);
			if (status < 0)
				kernelError(kernel_warn, "Unable to kill child process \"%s\" of parent process \"%s\"",
				            processQueue[count]->processName,
				            killProcess->processName);
			// Restart the loop
			count = 0;
			continue;
		}
	}

	// Now we look after killing the process with the Id we were passed

	// If this process is a thread, decrement the count of descendent
	// threads of its parent
	if (killProcess->type == proc_thread)
		decrementDescendents(killProcess);

	// Restore echo to the input stream, if applicable
	if (killProcess->textInputStream)
		killProcess->textInputStream->echo = 1;

	// Dismantle the process
	status = deleteProcess(killProcess);
	if (status < 0)
	{
		killProcess->state = proc_zombie;
		RELEASE_LOCK_PROCESS_QUEUE();
		// Eek, there was a problem deallocating something, we guess.
		// Simply mark the process as a zombie so that it won't be run
		// any more, but it's resources won't be 'lost'
		kernelError(kernel_error, "Couldn't delete process %d: \"%s\"",
		            killProcess->processId, killProcess->processName);
		return (status);
	}

	// If the target process is the idle process, spawn another one
	if (killProcess == idleProc)
	{
		spawnIdleThread();
	}

	RELEASE_LOCK_PROCESS_QUEUE();

	// Done.  Return success
	return (status = 0);
}

/****************************************************************************/

int kernelMultitaskerKillByName(const char *name, int force)
{
	// Try to kill all processes whose names match the one supplied

	int status = 0;
	kernelProcess *killProcess = NULL;

	// Make sure multitasking has been enabled
	if (!multitaskingEnabled)
		return (status = ERR_NOTINITIALIZED);

	ACQUIRE_LOCK_PROCESS_QUEUE();
	while ((killProcess = getProcessByName(name)))
	{
		status = kernelMultitaskerKillProcess(killProcess->processId, force);
	}
	RELEASE_LOCK_PROCESS_QUEUE();

	return (status);
}

/****************************************************************************/

int kernelMultitaskerKillAll(void)
{
	// This function is used to shut down all processes currently running.
	// Normally, this will be done during multitasker shutdown.  Returns
	// 0 on success, negative otherwise.

	int status = 0;
	kernelProcess *killProcess = NULL;
	int count;

	// Make sure multitasking has been enabled
	if (!multitaskingEnabled)
		return (status = ERR_NOTINITIALIZED);

	ACQUIRE_LOCK_PROCESS_QUEUE();

	// Kill all of the processes, except the kernel process and the current
	// process.  This won't kill the scheduler's task either.

	// Stop all processes, except the kernel process, and the current process.
	for (count = 0; count < numQueued; count ++)
		if (PROC_KILLABLE(processQueue[count]))
			processQueue[count]->state = proc_stopped;

	for (count = 0; count < numQueued; )
	{
		if (!PROC_KILLABLE(processQueue[count]))
		{
			count++;
			continue;
		}

		killProcess = processQueue[count];

		// Attempt to kill it.
		status = kernelMultitaskerKillProcess(killProcess->processId, 0); // no force
		if (status < 0)
		{
			// Try it with a force
			status = kernelMultitaskerKillProcess(killProcess->processId, 1); // force
			if (status < 0)
			{
				// Still errors?  The process will still be in the queue.
				// We need to skip past it.
				count++;
				continue;
			}
		}
	}

	RELEASE_LOCK_PROCESS_QUEUE();

	// Return success
	return (status = 0);
}

/****************************************************************************/

int kernelMultitaskerTerminate(int retCode)
{
	// This function is designed to allow a process to terminate itself 
	// normally, and return a result code (this is the normal way to exit()).
	// On error, the function returns negative.  On success, of course, it
	// doesn't return at all.

	int status = 0;
	kernelProcess *parent = NULL;
  
	// Make sure multitasking has been enabled
	if (!multitaskingEnabled)
		return (status = ERR_NOTINITIALIZED);

	// Don't do this inside an interrupt
	if (kernelProcessingInterrupt)
		kernelPanic("Cannot terminate() inside interrupt handler");

	ACQUIRE_LOCK_PROCESS_QUEUE();

	// Find the parent process before we terminate ourselves
	parent = getProcessById(kernelCurrentProcess->parentProcessId);

	if (parent)
	{
		// We found our parent process.  Is it blocking, waiting for us?
		if (parent->waitForProcess == kernelCurrentProcess->processId)
		{
			// It's waiting for us to finish.  Put our return code into
			// its blockingExitCode field
			parent->blockingExitCode = retCode;
			parent->waitForProcess = 0;
			parent->state = proc_ready;

			// Done.
		}
	}

	RELEASE_LOCK_PROCESS_QUEUE();

	while (1)
	{
		// If we still have threads out there, we don't dismantle until they are
		// finished
		if (!kernelCurrentProcess->descendentThreads)
			// Terminate
			kernelCurrentProcess->state = proc_finished;

		kernelMultitaskerYield();
	}

	return (status);
}

/****************************************************************************/
